package cn.wolfcode.wolf2w.business.service;

import cn.wolfcode.wolf2w.business.config.DeepSeekProperties;
import cn.wolfcode.wolf2w.business.domain.Strategy;
import cn.wolfcode.wolf2w.business.dto.OpenAIChatRequest;
import cn.wolfcode.wolf2w.business.dto.OpenAIChatResponse;
import cn.wolfcode.wolf2w.business.dto.OpenAIMessage;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;

@Slf4j
@Service
/// 核心功能是调用 DeepSeek 大模型 API，结合私有知识库生成回答。
public class DeepseekService {

    @Autowired
    private DeepSeekProperties deepSeekProperties;///存储 Deepseek API 的配置信息
    @Autowired
    private RestTemplate restTemplate;///Spring 提供的 HTTP 客户端工具，用于发送请求到 Deepseek API。

    /*
    * 查询deepseek获取结果
    * 参数1： 用户的基础问题
    * 参数2： 私库中查询回来的数据
    * 返回值： deepseek的应答结果
    * */

    ///接收用户问题和私有策略数据，调用 Deepseek API 并返回 AI 的回答，
    public String askWithStrategies(String question, List<Strategy> strategies){
        /// 步骤 1：构建系统提示词（约束大模型行为）
        /// 调用buildSystemPrompt方法，将私有库的strategies数据转换为大模型能理解的 “系统提示词”
        ///（定义 AI 的角色和回答规则）。
        String systemPrompt =  buildSystemPrompt(strategies);
        /// 步骤 2：创建消息对象（区分角色）
        /// 创建system角色的消息：role为"system"（告诉大模型这是系统指令），内容为上一步生成的systemPrompt。
        OpenAIMessage systemMessage = new OpenAIMessage("system", systemPrompt);
        /// 创建user角色的消息：role为"user"（表示这是用户的问题），content为用户输入的question。
        OpenAIMessage userMessage = new OpenAIMessage("user", question);
        ///步骤 3：组装消息列表（构建上下文）
        /// 创建消息列表messages，按顺序添加systemMessage（系统指令）和userMessage（用户问题），这是大模型的输入上下文。
        /// 顺序重要性：大模型会按消息顺序理解上下文，先接收系统指令，再接收用户问题，确保规则优先生效。
        List<OpenAIMessage> messages = new ArrayList<>();
        messages.add(systemMessage);
        messages.add(userMessage);
        /// 步骤 4：构建 API 请求参数（设置模型参数）
        /// 创建请求体对象requestBody，封装发送给 DeepSeek API 的所有参数。
        OpenAIChatRequest requestBody = new OpenAIChatRequest();
        /// 设置请求体的messages字段：传入上面组装的消息列表（上下文）。
        requestBody.setMessages(messages);
        /// 设置使用的模型名称：从配置deepSeekProperties中获取（如deepseek-chat等具体模型）。
        requestBody.setModel(deepSeekProperties.getModel());
        /// 设置temperature（温度）参数：0.3 表示大模型回答的随机性较低（值越低越确定，越高越灵活）。
        requestBody.setTemperature(0.3);
        /// 步骤 5：设置请求头（身份认证与格式声明）
        /// 创建 HTTP 请求头对象headers，用于设置请求的元数据。
        HttpHeaders headers = new HttpHeaders();
        /// 设置请求的内容类型为application/json：告诉服务器请求体是 JSON 格式。
        headers.setContentType(MediaType.APPLICATION_JSON);
        /// 设置 Bearer 认证头：Authorization: Bearer {apiKey}，从配置中获取apiKey用于 DeepSeek API 的身份验证。
        headers.setBearerAuth(deepSeekProperties.getApiKey());
        /// 步骤 6：封装请求实体（整合头与体）
        /// 创建HttpEntity对象，将请求体（requestBody）和请求头（headers）封装在一起，作为 HTTP 请求的完整数据。
        HttpEntity<OpenAIChatRequest> httpEntity = new HttpEntity<>(requestBody, headers);
        /// 步骤 7：发送请求到 Deepseek API
        /// 拼接 DeepSeek API 的完整地址：从配置中获取基础地址（baseUrl），加上聊天接口路径/chat/completions
        ///（DeepSeek 的标准聊天接口路径）。
        String url = deepSeekProperties.getBaseUrl()+"/chat/completions";
        /**
        * 向deepseek发送请求
        * 参数1： 请求地址
        * 参数2： 请求数据 请求头+请求体
        * 参数3： 应答类型
        * 返回值： deepseek返回的数据
        * */
        OpenAIChatResponse response = restTemplate.postForObject(url, httpEntity, OpenAIChatResponse.class);

        /// 步骤 8：处理响应并返回结果
        /// 检查响应有效性：若响应为null，或响应中的choices（回答选项列表）为null/ 空，则返回默认提示 “(AI 没有找到答案)”。
        if(response == null || response.getChoices() == null || response.getChoices().isEmpty()){
            return "(AI没有找到答案)";
        }
        /// 大模型响应（OpenAIChatResponse）中，choices字段是回答列表（通常包含 1 个或多个候选回答）。
        /// 取第一个回答（choices.get(0)），并提取其content字段（即大模型生成的具体回答文本）。
        /// 多重空值判断：避免因 API 返回异常（如网络问题、参数错误）导致的空指针异常，返回友好提示。
        OpenAIChatResponse.Choice choice = response.getChoices().get(0);
        /// 检查第一个选项的有效性：若选项为null，或选项中的消息为null，则返回默认提示。
        /// 返回 AI 生成的回答内容：从第一个选项的消息中提取content字段（即最终回答）。
        if(choice == null || choice.getMessage() == null){
            return "(AI没有找到答案)";
        }

        return choice.getMessage().getContent();

    }

    /// 将list变成字符串
    /// 私有方法，作用是该方法用于将私有知识库数据转换为大模型可理解的 “系统指令”，共 3 个步骤：
    private String buildSystemPrompt(List<Strategy> strategies) {
        /// 步骤 1：初始化字符串拼接器
        /// 创建StringJoiner对象，用于拼接字符串，分隔符为两个换行符（\n\n），使拼接后的文本格式更清晰。
        StringJoiner joiner = new StringJoiner("\n\n");
        /// 步骤 2：添加引导语（定义大模型角色与规则）
        /// 向joiner添加系统提示的引导语：
        joiner.add("你是一名知名的导游，请基于提供的私有知识库片段进行整理，形成一篇智能旅行推荐攻略，不要编造。如果资料不足请明确说明。\n\n" +
                "以下是来自私有知识库的片段（JSON):");
        /// 步骤 3：拼接私有知识库数据（以 JSON 格式）
        /// 遍历strategies列表：
        /// 用JSON.toJSONString(strategy)将每个Strategy对象转换为 JSON 字符串（方便大模型解析结构化数据）。
        /// 将 JSON 字符串添加到joiner（通过分隔符\n\n与其他内容分隔）。
        for (Strategy strategy : strategies) {
            String jsonString = JSON.toJSONString(strategy);
            joiner.add(jsonString);
        }
        /// 将joiner拼接的所有内容转换为字符串，作为系统提示词返回。
        return joiner.toString();
    }
    /*
    整个类的核心逻辑是：
       → 接收用户问题和私有旅行策略数据
        → 构建包含角色定义和私有数据的系统提示
         → 组装请求参数并发送到 DeepSeek API
          → 处理响应并返回 AI 生成的回答。通过这种方式，实现了 “私有知识库 + 大模型” 的结合，
            确保 AI 回答基于业务系统的真实数据，同时利用大模型的自然语言能力生成流畅的旅行推荐。
    **/



}
